package sany.iec.service.ems;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.openmuc.j60870.*;
import org.openmuc.j60870.ie.*;
import org.springframework.stereotype.Service;
import sany.iec.common.GlobalConstant;
import sany.iec.config.IecEmsServer;
import sany.iec.service.IecServerService;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @Author : 托马斯
 * @Date : 2025/7/2 9:33
 * @Description :
 */
@Service("EMS")
@Slf4j
@Getter
public class EmsServerServiceImpl implements IecServerService {

    @Resource(name = "EmsServer")
    private Server server;

    @Resource
    private IecEmsServer iecEmsServer;

    // 线程安全的连接列表
    @Getter
    private final List<Connection> activeConnections = new ArrayList<>();

    @PostConstruct
    public void init() throws IOException {
        startEmsServer();
    }

    @Override
    public void stopEmsServer() {
        log.info("停止ems服务~~~");
        server.stop();
    }

    @Override
    public void startEmsServer() throws IOException {
        log.info("启动EMS服务监听，监听端口：{}", iecEmsServer.getPort());
        server.start(new ServerEventListener() {
            @Override
            public ConnectionEventListener connectionIndication(Connection connection) {
                log.info("客户端连接成功: {}", connection.getLocalAddress());
                activeConnections.add(connection); // 添加到活跃连接列表

                return new ConnectionEventListener() {
                    // 当 客户端向服务端发送 ASdu（Application Service Data Unit）数据单元 时，会触发此方法。
                    // 接收遥测、遥信、遥控、遥测变化等实际业务数据。
                    @Override
                    public void newASdu(Connection connection, ASdu aSdu) {
                        InformationObject[] info = aSdu.getInformationObjects();
                        InformationElement[][] elements = info[0].getInformationElements();
                        InformationElement element = elements[0][0];
                        Object value = getValue(element);
                        log.info("{}-{} EMS接收到数据：COA - {},  IOA - {},  value - {}, type - {}", connection.getLocalAddress(), connection.getLocalPort(),
                                aSdu.getCommonAddress(), info[0].getInformationObjectAddress(), value, aSdu.getTypeIdentification());
                        try {
                            response(connection, aSdu);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        GlobalConstant.RECEIVE_DATA.putIfAbsent(connection.getLocalPort(), new HashMap<>());
                        GlobalConstant.RECEIVE_DATA.get(connection.getLocalPort()).put(info[0].getInformationObjectAddress(), Float.parseFloat(value.toString()));
                    }

                    // 当 客户端断开连接（主动或被动）时，触发此方法。
                    @Override
                    public void connectionClosed(Connection connection, IOException e) {
                        log.info("EMS客户端断开连接: {}  {}", connection.getLocalAddress(), connection.getLocalPort());
                        activeConnections.remove(connection); // 从列表中移除
                    }

                    // 当连接状态从“未就绪”变为“就绪”时，触发此方法。
                    @Override
                    public void dataTransferStateChanged(Connection connection, boolean b) {
                        log.info("EMS数据状态发生改变: {}  {}", connection.getLocalAddress(), connection.getLocalPort());
                    }
                };
            }

            @Override
            public void serverStoppedListeningIndication(IOException e) {
                log.error("EMS服务器停止监听", e);
            }

            @Override
            public void connectionAttemptFailed(IOException e) {
                log.error("EMS客户端连接失败", e);
            }
        });
    }


    private void response(Connection connection, ASdu aSdu) throws IOException {
        // 构造激活终止（ACT_TERM）回应，避免客户端没有得到相应报连接超时或者控制超时
        ASdu actTerm = new ASdu(
                aSdu.getTypeIdentification(),        // 同样类型
                false,
                CauseOfTransmission.ACTIVATION_CON,
                aSdu.isTestFrame(),
                aSdu.isNegativeConfirm(),
                aSdu.getOriginatorAddress(),
                aSdu.getCommonAddress(),
                aSdu.getInformationObjects()
        );

        connection.send(actTerm);
    }

    @Override
    public void removeConnection(Connection connection) {
        activeConnections.remove(connection);
    }

    @Override
    public List<Connection> getConnections() {
        return activeConnections;
    }

    private Object getValue(InformationElement element) {
        if (element instanceof IeNormalizedValue) {
            int value = ((IeNormalizedValue) element).getUnnormalizedValue();
            log.info("Normalized Value: {}", value);
            return value;
        } else if (element instanceof IeShortFloat) {
            float value = ((IeShortFloat) element).getValue();
            log.info("ShortFloat Value: {}", value);
            return value;
        } else if (element instanceof IeSinglePointWithQuality) {
            boolean value = ((IeSinglePointWithQuality) element).isOn();
            log.info("SinglePointWithQuality Value: {}", value);
            return value ? 1 : 0;
        } else if (element instanceof IeSingleCommand) {
            boolean value = ((IeSingleCommand) element).isCommandStateOn();
            log.info("IeSingleCommand Value: {}", value);
            return value ? 1 : 0;
        } else if (element instanceof IeQualifierOfInterrogation) {
            log.info("总召命令");
        } else if (element instanceof IeQualifierOfCounterInterrogation) {
            log.info("电能量召唤");
        } else if (element instanceof IeDoubleCommand) {
            int value = ((IeDoubleCommand) element).getCommandState().ordinal();
            log.info("IeDoubleCommand Value: {}", value);
            return value;
        }else {
            log.error("Unknown InformationElement type: " + element.getClass());
        }
        return 0.0f;
    }


}
